/*->c.batch */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>



#include "h.os"
#include "h.bbc"
#include "h.wimp"
#include "h.flex"


#include "h.wos"
#include "h.timex"
#include "h.swis"

#include "h.main"
#include "h.ram"
#include "h.mym"
#include "h.trans"


#include "h.file"
#include "h.serial"
#include "h.view"
#include "h.ftp"
#include "h.key"

#include "h.dir"

#include "h.xext"
#include "h.bf"

#include "h.batch"

/****************************************************************************/


/* Hearsay sends this when it is going to download a new file */

#define FLINIT        512

/* Remote app sends this to us if it wants to receive the file */

#define FLSENDDATA    513

/* Remote app sends this to us if it wants to stop */

#define FLSTOP        514

/* Hearsay sends this when download is finished */

#define FLFIN         515


/* Hearsay sends this when it transfers stuff */

#define FLWRITE       516




int filelinktask;
int filelinkbuffer;
int filelinkactive;
int filelinksize;

void filelinkin(void)
{
 switch(wimpevent.data.msg.data.words[0])
 {
  case FLSENDDATA:
                 /* dprintf(4,"Hsy rx send data"); */

                  filelinkactive=1;
                  filelinkbuffer=wimpevent.data.msg.data.words[1];
                  filelinktask=wimpevent.data.msg.hdr.task;
                  filelinksize=wimpevent.data.msg.data.words[2];
                  break;

  case     FLSTOP:
                  /* dprintf(4,"Hsy rx stop"); */

                  filelinkactive=0;
                  break;

 }
}


void filelinkstart(int type,char * rname,char * lname)
{
 wimp_msgstr msg;
 int         llen;
 int         rlen;

 llen=strlen(lname);
 rlen=strlen(rname);

 msg.hdr.size=((48+8+rlen+1
                    +llen+1+4) & 0xFFFFFFFC);

 msg.hdr.task=taskhandle;
 msg.hdr.your_ref=0;
 msg.hdr.action=SPARKMESS;

 msg.data.words[0]=FLINIT;
 msg.data.words[1]=type;
 strcpy(msg.data.chars+8,lname);

 if((llen+rlen)<220)
   strcpy(msg.data.chars+8+llen+1,rname);
 else
   strcpy(msg.data.chars+8+llen+1,"");

 wimp_sendmessage(17,&msg,0);
}


void filelinkwrite(char * buff,int size)
{
 os_regset   rx;
 os_error  * ep;
 wimp_msgstr msg;

 if(filelinkactive)
 {
  rx.r[0]=taskhandle;          /* src taskhandle         */
  rx.r[1]=(int)buff;           /* src buffer             */
  rx.r[2]=filelinktask;        /* destination taskhandle */
  rx.r[3]=filelinkbuffer;      /* destination buffer     */
  rx.r[4]=size;

  ep=os_swix(Wimp_TransferBlock,&rx);

  if(!ep)
  {
   msg.hdr.size=((48+8) & 0xFFFFFFFC);
   msg.hdr.task=taskhandle;
   msg.hdr.your_ref=0;
   msg.hdr.action=SPARKMESS;

   msg.data.words[0]=FLWRITE;
   msg.data.words[1]=size;

   wimp_sendmessage(17,&msg,filelinktask);
  }
  else
  {
   /* report(ep); */
   /* dprintf(0,"buff=%x",filelinkbuffer); */

   filelinkactive=0;
  }
 }
}


void filelinkfinished(void)
{
 wimp_msgstr msg;

 if(filelinkactive)
 {
  msg.hdr.size=((48+4) & 0xFFFFFFFC);
  msg.hdr.task=taskhandle;
  msg.hdr.your_ref=0;
  msg.hdr.action=SPARKMESS;

  msg.data.words[0]=FLFIN;

  wimp_sendmessage(17,&msg,0);
 }
}



/*****************************************************************************/

int autoremove;        /* auto remove files from batch on send  */
int autosend;          /* auto send files on batch entry        */
int usetypealias;      /* use type alias file to set extensions */
int retainbatch=1;     /* retain batch info    */
int batchprompt;       /* prompt for filenames */
int batchmodded;       /* modified bat         */
int completewarning;   /* warn when complete   */
int batoverwrite;      /* over write on RX     */
int batdiscard;        /* discard short files  */

/*****************************************************************************/

void modbatch(int code)
{
 batchmodded=1;
 if(code) savebatch();
}


/* return index of next file to send */
/* return -1 if there isn't one      */

int txbatchnext(void)
{
 int tx=0;

 while(tx<vnofiles[TXBFILE])
 {
  if(vtable[TXBFILE][tx].state==PREADY) return(tx);
  tx++;
 }
 return(-1);
}


/* return number of files in tx batch */

int txbatchfiles(void)
{
 int n;
 int i;

 n=0;

 for(i=0;i<vnofiles[TXBFILE];i++) 
   if(vtable[TXBFILE][i].state==PREADY) n++;

 return(n);
}


/*****************************************************************************/

typedef struct
{
 int   type;
 char  extension[8];
} typemap;


typemap * txtypemap;
typemap * rxtypemap;
int       rxsize;
int       txsize;
typemap   txdeflt;
typemap   rxdeflt;



static int addalias(typemap ** map,int * size,int type,char * extension)
{
 int i=*size;

 if(flex_extend((flex_ptr)map,(i+1)*sizeof(typemap)))
 {
  (*map)[i].type=type;
  strcpy((*map)[i].extension,extension);
  *size=i+1;
  return(1);
 }
 else
  return(0);
}


void addrxalias(int fp)
{                                                                                int type;
 char extension[32];

 type=stack[fp];
 strcpy(extension,stringptr(stack[fp+1]));

 if(!addalias(&rxtypemap,&rxsize,type,extension)) zraise(ZMEM,"{ALIAS}");
}


void addtxalias(int fp)
{
 int type;
 char extension[32];

 type=stack[fp];
 strcpy(extension,stringptr(stack[fp+1]));

 if(!addalias(&txtypemap,&txsize,type,extension)) zraise(ZMEM,"{ALIAS}");
}


void setdefltalias(int fp)
{
 rxdeflt.type=stack[fp];
 strcpy(txdeflt.extension,stringptr(stack[fp+1]));
}



void boottypemaps(void)
{
 flex_alloc((flex_ptr)&txtypemap,0);
 flex_alloc((flex_ptr)&rxtypemap,0);

 rxdeflt.type=0xFFD;
 strcpy(txdeflt.extension,transtoken("ARM"));
}



int rxtypefrommap(char * extension)
{
 int i;

 for(i=0;i<rxsize;i++)
   if(!cstrcmp(extension,rxtypemap[i].extension)) return(rxtypemap[i].type);

 return(rxdeflt.type);
}



char * txextfrommap(int type)
{
 int              i;
 static char ext[8];

 for(i=0;i<txsize;i++)
 {
  if(type==txtypemap[i].type)
  {
   strcpy(ext,txtypemap[i].extension);
   return(ext);
  }
 }
 return(txdeflt.extension);
}



/*****************************************************************************/


void bootbatch(void)
{
 FILE * fp;

 setnofiles(TXBFILE,0);
 setnofiles(RXBFILE,0);

 fp=fopen(path(BATP),"rb");
 if(fp)
 {
  for(view=0;view<BATN;view++)
  {
   fread(&vnofiles[view],sizeof(int),1,fp);
   setnofiles(view,vnofiles[view]);
   fread(vtable[view],sizeof(ficon),vnofiles[view],fp);
  }
  fclose(fp);
 }

 boottypemaps();
}


void deletebatch(void)
{
 remove(path(BATP));
}


void savebatch(void)
{
 FILE * fp;
 int    view;

 if(retainbatch && batchmodded)
 {
  fp=fopen(path(BATP),"wb");
  if(fp!=NULL)
  {
   for(view=0;view<BATN;view++)
   {
    fwrite(&vnofiles[view],sizeof(int),1,fp);
    fwrite(vtable[view],sizeof(ficon),vnofiles[view],fp);
   }
   batchmodded=0;
   fclose(fp);
  }
 }
}





void opentxbatch(void)
{
 view=TXBFILE;
 openview();
}



void openrxbatch(void)
{
 view=RXBFILE;
 openview();
}


/* return 1 on OK, 0 on error */

int setrxpath(char * filename)
{
 char s[256];
 char * q;

 sprintf(s,"set %s ",path(RXBP));
 q=s+strlen(s);

 strcpy(q,filename);
 os_cli(s);

 return(1);
}



/* called when a file is dropped into a window */
/* return 0      on error                      */
/* return insert posn+1                        */
/* view/file already set up                    */
/* adding to TX batch                          */

int batchload(int type,char * filename,int xvolatile)
{
 ficon   fi;
 int     i;
 int     ow;
 char  * q;
 int     seendot;

 if(view==RXBFILE)          return(0);
 if(type==DIR || type==APP) return(0);

/* strcpy(fi.stat.name,leaf(filename)); */
 strcpy(fi.lname,filename);
 strcpy(fi.rname,leaf(filename));

 seendot=0;
 q=fi.rname;
 while(*q)
 {
  if(*q=='/')
  {
   *q='.';
   seendot=1;
  }
  q++;
 }

 if(usetypealias && !seendot)
 {
  q=txextfrommap(type);
  if(strlen(q))
  {
   strcat(fi.rname,".");
   strcat(fi.rname,q);
  }
 }


 for(i=0;i<vnofiles[view];i++)
 {
  if(cstrcmp(leaf(vtable[view][i].lname),leaf(fi.lname))>=0) break;
 }

 ow=(i<vnofiles[view]) && !cstrcmp(vtable[view][i].lname,fi.lname);

 stat(filename,&fi.stat);

 getcurtime(&fi.otime);
 fi.ctime=fi.otime;

 fi.protocol=PNONE;
 fi.state=PREADY;
 fi.temporary=xvolatile;

 strcpy(fi.info,"");
 strcpy(fi.sys,"");

 insertentry(i,&fi,ow);

 modbatch(1);

 if(!ftpactive && autosend) addzeroevent(FTPASEND);

 return(i+1);
}




/* file/view not set up                                         */
/* have to provide either view for root, or a dummy unused view */

int addtotxbatch(int type,char * filename,int xvolatile,int popup)
{
 int code;

 view=TXBFILE;

 code=batchload(type,filename,xvolatile);

 if(!autosend && popup) opentxbatch();

 return(code);
}



/*****************************************************************************/

static buffer bfo;
static buffer bfi;

#define HSYDEFBUFFSIZE 0x1000

static int    ftprxsize=HSYDEFBUFFSIZE;
static int    ftptxsize=HSYDEFBUFFSIZE;


/* takes a filename, and a path                                  */
/* returns a new filename, which lives down the path, but is new */

void makefilename(char * oldname,char * newname,char * path,int over)
{
 int        i;
 int        j;
 int        lentemp;
 char    *  p;
 char       newfile[FSMAXLEAF];
 char       temp[16];


 strncpy(newfile,oldname,FSMAXLEAF-1);
 newfile[FSMAXLEAF-1]=0;


 j=0;
 for(i=0;i<strlen(newfile);i++)
 {
  newfile[i] &=0x7F;

  switch(newfile[i])
  {
    case '.':
             newfile[j]='/';
             break;

    case ' ':
             newfile[j]=' ';
             break;

    case '#':
             newfile[j]='?';
             break;

    case '&':
             newfile[j]='+';
             break;

    case '@':
             newfile[j]='=';
             break;

    case '%':
             newfile[j]=';';
             break;

    case '$':
             newfile[j]='<';
             break;

    case '^':
             newfile[j]='>';
             break;


    case '"':
    case '*':
    case ',':
    case ':':
    case'\\':
    case '|':
    case 127:
             newfile[j]='X';
             break;

     default:
             newfile[j]=newfile[i];
             break;
  }
  j++;
 }

 if(strlen(newfile)==0) strcpy(newfile,"DEFAULT");

 strcpy(newname,path);
 if(*newname) strcat(newname,".");
 p=newname+strlen(newname);
 *temp=0;

 for(i=0;i<1000;i++)
 {
  lentemp=strlen(temp);
  strcpy(p,temp);
  strcpy(p+lentemp,newfile);
  if(over || (!fexists(newname))) break;
  sprintf(temp,"%02d",i);
 }

}



static void ftpfileopen(FILE * fp,char * filename,int txrx)
{
 if(fp) xexec("sys_ftp_open",filename,&txrx,NULL);
}


static void ftpfileclose(char * filename,int state)
{
 xexec("sys_ftp_close",filename,&state,NULL);
}



/* called to open a file that we are downloading   */
/* and want adding to the RX batch                 */

static FILE * batchfopen(char * filename,int length,int * flags,int * bn)
{
 FILE     * fp;
 os_error * errpoi;
 ficon      fi;
 int        i;
 char     * q;
 int        type;
 int        ow;


 if(flags && ((*flags) & FTPOPENRESUME))
 {                                      /* look up remote filename */
  for(i=0;i<vnofiles[RXBFILE];i++)
   if(!cstrcmp(filename,vtable[RXBFILE][i].rname))
   {
    if(bn) *bn=i;

    if(bf_open(vtable[RXBFILE][i].lname,'u',ftprxsize,&bfo)) fp=NULL;
    else
    {
     bf_seek(&bfo,0,SEEK_END);
     fp=(FILE*)&bfo;
    }
    return(fp);
   }
 }
 else
 if(flags && ((*flags) & FTPOPENOVER))
 {
  for(i=0;i<vnofiles[RXBFILE];i++) 
   if(!cstrcmp(filename,vtable[RXBFILE][i].lname))
   {
    if(bn) *bn=i;

    if(bf_open(vtable[RXBFILE][i].lname,'w',ftprxsize,&bfo)) fp=NULL;
    else                                                     fp=(FILE*)&bfo;

    return(fp);
   }
 }

 if(flags) *flags=0;

 type=DATA;

 if(batchprompt)
 {
  strcpy(fi.lname,filename);
  strcpy(fi.rname,filename);
 }
 else
 {
  makefilename(filename,fi.lname,path(RXBP),batoverwrite);

  if(usetypealias)
  {
   q=filename+strlen(filename);
   while(q!=filename)
   {
    q--;
    if(*q=='.') break;
   }
  
   if(*q=='.') type=rxtypefrommap(q+1);
  }
 }

 errpoi=createfile(fi.lname,length,type);
 if(errpoi) return(NULL);

 strcpy(fi.rname,filename);
/* strcpy(fi.stat.name,leaf(fi.lname)); */
 stat(fi.lname,&fi.stat);

 strcpy(fi.info,"");
 strcpy(fi.sys,sysname);

 fi.protocol=PNONE;
 fi.state=PSHORT;
 fi.temporary=0;

 view=RXBFILE;

 for(i=0;i<vnofiles[RXBFILE];i++)
 {
  if(cstrcmp(leaf(vtable[view][i].lname),leaf(fi.lname))>=0) break;
 }

 ow=(i<vnofiles[view]) && !cstrcmp(vtable[view][i].lname,fi.lname);

 insertentry(i,&fi,ow);
 modbatch(0);
 
 if(bn) *bn=i;

 if(bf_open(fi.lname,'w',ftprxsize,&bfo)) fp=NULL;
 else                                     fp=(FILE*)&bfo;

 return(fp);
}




/***************************************************************************/
/*
 Code to handle Batch menus
*/
/***************************************************************************/

#ifdef NEVER

void decodebatcfg(int m3)
{
 switch(m3)
 {
  case 0:
         autoremove^=1;
         break;

  case 1:
         autosend^=1;
         break;

  case 2:
         usetypealias^=1;
         break;

  case 3:
         if(retainbatch) deletebatch();
         retainbatch^=1;
         break;

  case 4:
         batchprompt^=1;
         break;

  case 6:
         completewarning^=1;
         break;

  case 7:
         batoverwrite^=1;
         break;

  case 8:
         batdiscard^=1;
         break;
 }
}



void setpopbatch(void)
{
 tickst(batcfg_menu,0,autoremove);
 tickst(batcfg_menu,1,autosend);
 tickst(batcfg_menu,2,usetypealias);
 tickst(batcfg_menu,3,retainbatch);
 tickst(batcfg_menu,4,batchprompt);
 tickst(batcfg_menu,6,completewarning);
 tickst(batcfg_menu,7,batoverwrite);
 tickst(batcfg_menu,8,batdiscard);
}

#endif


static int tempautoremove;
static int tempautosend;
static int tempusetypealias;
static int tempretainbatch;
static int tempbatchprompt;
static int tempcompletewarning;
static int tempbatoverwrite;
static int tempbatdiscard;


int saverxpath(char * filename)
{
 char * p;

 p=leaf(filename);
 if(p!=filename)
 {
  *(p-1)=0;
 }

 writeicon(whandle[TBATCONFIG],11,filename);
 return(1);
}




void batconfigdragend(void)
{
 getpointer();
 dragaspritestop();

 /* find out if we are loading stuff back into our selves */

 if(mhandle==whandle[TBATCONFIG]) return;
 else
 sendsave(DATA,transtoken("RXPATH"));

 setsavetype(SAVERXPATH);
}



static void batconfigclose(void)
{
 autoremove=tempautoremove;
 autosend=tempautosend;
 usetypealias=tempusetypealias;
 retainbatch=tempretainbatch;
 batchprompt=tempbatchprompt;
 completewarning=tempcompletewarning;
 batoverwrite=tempbatoverwrite;
 batdiscard=tempbatdiscard;
 if(!retainbatch) deletebatch();
 setrxpath(iconaddr(whandle[TBATCONFIG],11));
}



void batconfigicon(void)
{
 int handle=whandle[TBATCONFIG];

 switch(icon)
 {
  case 0:
         selectst(handle,0,tempautoremove^=1);
         break;

  case 1:
         selectst(handle,1,tempautosend^=1);
         break;

  case 2:
         selectst(handle,2,tempusetypealias^=1);
         break;

  case 3:
         selectst(handle,3,tempretainbatch^=1);
         break;

  case 4:
         selectst(handle,4,tempbatchprompt^=1);
         break;

  case 5:
         selectst(handle,5,tempcompletewarning^=1);
         break;

  case 6:
         selectst(handle,6,tempbatoverwrite^=1);
         break;

  case 7:
         selectst(handle,7,tempbatdiscard^=1);
         break;

 case 10:
         if(buttons==0x40 || buttons==0x10) savedrag();
         break;

 case 12:
         batconfigclose();
         if(buttons==0x4) zapmenu();
         break;
 }
}


void batconfigkey(int * key)
{
 if(*key==RETURN) batconfigclose();
 else
 if(*key!=ESCAPE) return;
 zapmenu();
 *key=-1;
}



int setupbatconfig(void)
{
 int handle=createwindow(TBATCONFIG);

 tempautoremove=autoremove;
 tempautosend=autosend;
 tempusetypealias=usetypealias;
 tempretainbatch=retainbatch;
 tempbatchprompt=batchprompt;
 tempcompletewarning=completewarning;
 tempbatoverwrite=batoverwrite;
 tempbatdiscard=batdiscard;

 selectst(handle,0,tempautoremove);
 selectst(handle,1,tempautosend);
 selectst(handle,2,tempusetypealias);
 selectst(handle,3,tempretainbatch);
 selectst(handle,4,tempbatchprompt);
 selectst(handle,5,tempcompletewarning);
 selectst(handle,6,tempbatoverwrite);
 selectst(handle,7,tempbatdiscard);

 writeicon(handle,11,path(RXBP));

 return(handle);
}


/*****************************************************************************/

static char opcode[48];
static int  totlength;
static int  bytessofar;
static int  blockssofar;
static int  retries;
static int  totretries;
static int  starttime;
static int  curtime;
static int  tottime;
static int  rate;
static int  files;
static int  blockmode;
static int  barlength;
static int  maxbarlength;


#define TBT1   14
#define TBT2   17
#define TBB1   18
#define TBB2   19
#define TBMODE 5
#define TBRATE 7
#define TBFILE 9
#define TBRET  11
#define TBINFO 12
#define TBBAR  20
#define TBCONT 15
#define TBCAN  16
#define TBBYTE 2



static void writeretries(void)
{
 if(tranboxopen) writeiconf(whandle[TRANBOX],TBRET,"%d/%d",retries,totretries);
}


static void writerate(void)
{
 int newrate;

 if(bytessofar==0 || zerotime==starttime)
   newrate=0;
 else
   newrate=(bytessofar*100)/(zerotime-starttime);

 if(rate!=newrate && tranboxopen)
 {
  writeiconf(whandle[TRANBOX],TBRATE,"%d",newrate);
  rate=newrate;
 }
}


static void writectime(void)
{
 int newctime=(zerotime-starttime)/100;

 if(curtime!=newctime && tranboxopen)
 {
  curtime=newctime;
  writehms(iconaddr(whandle[TRANBOX],TBT1),curtime*100);
  seti(whandle[TRANBOX],TBT1,0,0);
 }
}




void tranboxredrawsub(wimp_redrawstr * redrawstr,int more)
{
 int ox;
 int oy;

 while(more)
 { 
  ox=redrawstr->box.x0-redrawstr->scx;
  oy=redrawstr->box.y1-redrawstr->scy;

  geti(whandle[TRANBOX],TBBAR);
  if(iconinredraw(redrawstr))
  {
   wimp_setcolour(10);
   bbc_rectanglefill(ox+ix0,oy+iy0,barlength,iy1-iy0-deltay);
  }

  wimp_get_rectangle(redrawstr,&more);
 }
}



void tranboxredraw(void)
{
 int            more;
 wimp_redrawstr rblock;

 rblock.w=ewindow;
 wimp_redraw_wind(&rblock,&more);
 tranboxredrawsub(&rblock,more);
}



static void writebar(void)
{
 int            temp;
 int            more;
 wimp_redrawstr rblock;
 wimp_icon      isblock;

 if(blockmode)
 {
  temp=(maxbarlength*blockssofar)/blockmode;
 }
 else
 if(totlength)
 {
  temp=(maxbarlength*bytessofar)/totlength;
 }
 else return;

 if((temp & maskx)!=(barlength & maskx))
 {
  barlength=temp;
  if(barlength>maxbarlength) barlength=maxbarlength;

  wimp_get_icon_info(whandle[TRANBOX],TBBAR,&isblock);
  rblock.w=whandle[TRANBOX];
  rblock.box=isblock.box;
  wimp_update_wind(&rblock,&more);
  tranboxredrawsub(&rblock,more);
 }
}



static void writeblocks(void)
{
 if(tranboxopen && blockmode)
 {
  writeiconf(whandle[TRANBOX],TBB1,"%d",blockssofar);
  writebar();
 }
}


static void writebytes(void)
{
 if(tranboxopen && !blockmode)
 {
  writeiconf(whandle[TRANBOX],TBB1,"%d",bytessofar);
  writebar();
 }
}


static void startfile(int length,int bsize,int rate)
{
 int handle=whandle[TRANBOX];
 char   * p;

 if(tranboxopen)
 {
  totlength=length;                                                               bytessofar=0;
  blockssofar=0;
  barlength=0;
  blockmode=bsize;

  seti(handle,TBBAR,0,0);
  writeiconf(handle,TBFILE,"%d",++files);

  if(bsize)
  {
   writeiconf(handle,TBB2,"%d",bsize);
   p=transtoken("BLOCK");
   if(strcmp(iconaddr(handle,TBBYTE),p)) writeicon(handle,TBBYTE,p);
   writeblocks();
  }
  else
  {
   writeiconf(handle,TBB2,"%d",totlength);

   p=transtoken("BYTES");
   if(strcmp(iconaddr(handle,TBBYTE),p)) writeicon(handle,TBBYTE,p);
  }

  starttime=zerotime;

  if(!rate) tottime=0;
  else      tottime=length/rate;

  curtime=-1;
  writectime();
  writehms(iconaddr(handle,TBT2),tottime*100);
  seti(handle,TBT2,0,0);

  totretries=0;
  retries=0;
  writeretries();

  writerate();
  writebytes();
 }
}


static void writefilename(char * filename)
{
 if(tranboxopen)
 {
  char string[256];
  sprintf(string,"%s <%s>",opcode,filename);
  writetitle(TRANBOX,string);
  refreshwindowtitle(whandle[TRANBOX]);
 }
}


void ftpsetmode(char * mode)
{
 char temp[64];

 if(tranboxopen)
 {
  strcpy(temp,mode);
  trans(temp,sizeof(temp));

  if(strcmp(iconaddr(whandle[TRANBOX],TBMODE),temp))
                              writeicon(whandle[TRANBOX],TBMODE,temp);
 }
}



/* called to increment number of retries */

void ftpretinc(void)
{
 totretries++;
 retries++;
 if(tranboxopen) writeretries();
}



/* called when a good block is received */

void ftpbloinc(void)
{
 if(tranboxopen)
 {
  blockssofar++;
  if(retries)
  {
   retries=0;
   writeretries();
  }
  writeblocks();
 }
}



void tranboxicon(void)
{
 switch(icon)
 {
   case TBCAN:
              ftp_ok=0;
              break;

  case TBCONT:
              ftp_cont=0;
              break;
 }
}



/*****************************************************************************/


/* called when an ftp is opening a file to send it */
/* bn is number in TX batch                        */
/* bsize is size of file in blocks                 */
/* giving this, implies that ftp can use blocks    */

FILE * ftpopenread(int bn,int bsize)
{
 FILE * fp;

 view=TXBFILE;

 if(bf_open(vtable[TXBFILE][bn].lname,'r',ftptxsize,&bfi)) fp=NULL;
 else                                                      fp=(FILE*)&bfi;

 if(fp)
 {
  startfile(vtable[TXBFILE][bn].stat.length,bsize,txbyterate);

  writefilename(leaf(vtable[TXBFILE][bn].lname));

  getcurtime(&vtable[TXBFILE][bn].otime);
  vtable[TXBFILE][bn].ctime=vtable[TXBFILE][bn].otime;

  vtable[TXBFILE][bn].state=POPEN;
  vtable[TXBFILE][bn].protocol=ftprotocol;
  viewupdatetexticon(bn);
  strcpy(vtable[TXBFILE][bn].info,"");
  strcpy(vtable[TXBFILE][bn].sys,sysname);

  vobject[TXBFILE]=bn;

  ftpfileopen(fp,vtable[TXBFILE][bn].lname,0);
 }

 return(fp);
}



/* called after file has been sent              */
/* after this is called, the file may have gone */

int ftpcloseread(FILE * fp,char * message,int flags)
{
 int code;
 int bn=vobject[TXBFILE];

 code=(bf_close((buffer*)fp,NULL)!=NULL);

 if(bn>=0)
 {
  view=TXBFILE;

  getcurtime(&vtable[TXBFILE][bn].ctime);

  if(flags & FTPCLOSEERROR) vtable[TXBFILE][bn].state=PREADY;
  else                      vtable[TXBFILE][bn].state=PSENT;
  modbatch(1);

  if(message) 
  {
   strcpy(vtable[TXBFILE][bn].info,message);
   trans(vtable[TXBFILE][bn].info,VINFOSIZE);
  }

  viewupdatetexticon(bn);

  ftpfileclose(vtable[TXBFILE][bn].lname,vtable[TXBFILE][bn].state);

  if((!(flags & FTPCLOSEERROR)) && autoremove)
  {
   deleteentry(bn,0);
  }
 }
 return(code);
}




int ftpread(void * buff,int size,int number,FILE * fp)
{
 int code;

 if(bf_readn((buffer*)fp,buff,size*number,&code)) code=0;
 else                                             code/=size;

 if(code*size)
 {
  bytessofar+=code*size;
  writerate();
  writebytes();
  writectime();
 }

 return(code);
}



int ftpreadeof(FILE * fp)
{
 int code;

 bf_eof((buffer*)fp,&code);

 return(code);
}


int ftpreaderror(FILE * fp)
{
/* int code;

 code=ferror(fp); */

 fp=fp;

 return(0);
}



int ftpreadfseek(FILE *stream, long int offset,int whence)
{
 int code;

 code=(bf_seek((buffer*)stream,(int)offset,whence)!=NULL);

 bf_tell((buffer*)stream,&bytessofar);

 writerate();
 writebytes();
 writectime();

 return(code);
}



static int gotname;

/* return 1 on success 0 on failure */ 

int saveftpname(char * filename)
{
 gotname=0;
 strcpy(iconaddr(whandle[SAVEFILE0],1),filename);
 return(1);
}


/* called whan an ftp is opening a file for receive                          */
/* the desired length for the file, is passed in *len on entry               */
/* on entry, flags show if file is to be opened for append                   */
/* on exit,  flags show if file already existed                              */
/* bsize is size of file in blocks                                           */

int ftpstamp;


FILE * ftpopenwrite(char * rname,int * bnp,int bsize,
                                                int len,int * flags,int * hdr)
{
 FILE * fp;
 int    bn;
 int    handle;

 if(batchprompt && (*flags)!=FTPOPENRESUME && (*flags)!=FTPOPENOVER)
 {                                            /* hi risk stuff here */
  handle=setsave(SAVEFTPNAME);

  strcpy(iconaddr(whandle[SAVEFILE0],1),rname);
  rname=iconaddr(whandle[SAVEFILE0],1);

  menuwindow(handle);

  gotname=1;
  while(gotname)
  {
   pollt();
   getw(whandle[SAVEFILE0]);
   if(!(wflags & 0x10000)) break;
  }

  if(gotname) return(NULL);

  closedownt(SAVEFILE0);
 }


 fp=batchfopen(rname,len,flags,&bn);

 if(!fp) return(fp);

 view=RXBFILE;

 vobject[RXBFILE]=bn;

 if((*flags)!=FTPOPENOVER) startfile(len,bsize,rxbyterate);

 writefilename(leaf(vtable[RXBFILE][bn].lname));

 if(hdr)
 {
  if(*hdr & 0x1) vtable[RXBFILE][bn].stat.load=*(hdr-1);
  if(*hdr & 0x2) vtable[RXBFILE][bn].stat.exec=*(hdr-2);
  ftpstamp=1;
 }
 else
  ftpstamp=0;

 getcurtime(&vtable[RXBFILE][bn].otime);
 vtable[RXBFILE][bn].ctime=vtable[RXBFILE][bn].otime;

 vtable[RXBFILE][bn].state=POPEN;
 vtable[RXBFILE][bn].protocol=ftprotocol;
 vtable[RXBFILE][bn].temporary=0;

 viewupdatetexticon(bn);

 if(bnp) *bnp=bn;

 ftpfileopen(fp,vtable[RXBFILE][bn].lname,1);

 if(fp) filelinkstart(filetype(vtable[RXBFILE][bn].stat.load),
                               vtable[RXBFILE][bn].rname,
                               vtable[RXBFILE][bn].lname);

 return(fp);
}




int ftpclosewrite(FILE * fp,char * message,int flags)
{
 int   code;
 int   bn=vobject[RXBFILE];
 fstat fs;

 code=(bf_close((buffer*)fp,NULL)!=NULL);

 if(bn>=0)
 {
  getcurtime(&vtable[RXBFILE][bn].ctime);

  if(flags & FTPCLOSEERROR) vtable[RXBFILE][bn].state=PSHORT;
  else                      vtable[RXBFILE][bn].state=PRECVD;

  view=RXBFILE;

  strcpy(vtable[RXBFILE][bn].info,message);
  trans(vtable[RXBFILE][bn].info,VINFOSIZE);

  stat(vtable[RXBFILE][bn].lname,&fs);
 /* vtable[RXBFILE][bn].stat.length=fs.length; */

  if(ftpstamp)
  {
   fs.load=vtable[RXBFILE][bn].stat.load;
   fs.exec=vtable[RXBFILE][bn].stat.exec;
   stamp(vtable[RXBFILE][bn].lname,&fs);
  }

  stat(vtable[RXBFILE][bn].lname,&vtable[RXBFILE][bn].stat);

  viewupdatetexticon(bn);
  setspritesr(bn);

  ftpfileclose(vtable[RXBFILE][bn].lname,vtable[RXBFILE][bn].state);

  if((flags & FTPCLOSEERROR) && batdiscard)
  {
   deleteentry(bn,1);
  }

  modbatch(1);

  filelinkfinished();
 }

 return(code);
}




int ftpwrite(void * buff,int size,int number,FILE * fp)
{
 int code;

 if(bf_writen((buffer*)fp,buff,number*size,&code)) code=0;
 else                                              code/=size;

 if(code*size)
 {
  bytessofar+=code*size;
  writerate();
  writebytes();
  writectime();
 }

 filelinkwrite(buff,size*number);

 return(code);
}


int ftpwriteeof(FILE * fp)
{
 int code;

 bf_eof((buffer*)fp,&code);

 return(code);
}


int ftpwriteerror(FILE * fp)
{
/* int code; */

/* code=ferror(fp); */

 fp=fp;

 return(0);
}



int ftpwritefseek(FILE *stream, long int offset, int whence)
{
 int code;

 code=(bf_seek((buffer*)stream,(int)offset,whence)!=NULL);

 bf_tell((buffer*)stream,&bytessofar);

 writerate();
 writebytes();
 writectime();

 return(code);
}



int ftpwritetell(FILE *stream)
{
 int posn;
 bf_tell((buffer*)stream,&posn);
 return(posn);
}



void ftpopenbox(char * what)
{
 int handle=whandle[TRANBOX];
 strcpy(opcode,what);
 writeicon(handle,TBB1,"");
 writeicon(handle,TBB2,"");
 writeicon(handle,TBT1,"");
 writeicon(handle,TBT2,"");
 writeicon(handle,TBMODE,"");
 writeicon(handle,TBINFO,"");
 writeicon(handle,TBFILE,"");
 writeicon(handle,TBRATE,"");
 writeicon(handle,TBRET,"");

 geti(handle,TBBAR);
 maxbarlength=ix1-ix0;

 barlength=0;
 seti(handle,TBBAR,0,0);
 files=0;
}




void ftpinfo(char * format, ...)
{
 va_list args;
 char v[256];
 int len;
 char * string=v;

 va_start(args, format);

 vsprintf(v, format, args);

 trans(v,sizeof(v));

 len=strlen(string);
 if(len>31) string+=len-31; 
 if(tranboxopen && strcmp(string,iconaddr(whandle[TRANBOX],TBINFO))) 
                            writeicon(whandle[TRANBOX],TBINFO,string);
 va_end(args);
}



void ftpwarning(void)
{
 if(completewarning) bbc_vdu(7);
}


/*****************************************************************************/
/* script language interface */


void trashbatch(int view)
{
 int i;

 for(i=0;i<vnofiles[view];i++)
   if(vtable[view][i].temporary) remove(vtable[view][i].lname);

 if(vactive[view]) trashicons(i);

 vnofiles[view]=0;
 setnofiles(view,0);

 refreshallviews();

 modbatch(1);
}



void emptybatch(int fp)
{
 if(stack[fp]==0) trashbatch(TXBFILE);
 else 
 if(stack[fp]==1) trashbatch(RXBFILE);            
}



void batchremove(int fp)
{
 int n;

 view=stack[fp];
 n=stack[fp+1]-1;

 if(view==0 || view==1)
 {
/*  dprintf(0,"n=%d nof=%d",n,vnofiles[view]);  */
  if(n>=0 && n<vnofiles[view])
  {
   deleteentry(n,0);
  }

  modbatch(1);
 }
}



int addtobatch(int fp)
{
 char  name[256];
 fstat f;
 int   posn;

 strcpy(name,stringptr(stack[fp]));
 stat(name,&f);
 posn=addtotxbatch(f.type,name,0,0);
 return(posn);
}



int batchgetname(int fp)
{
 int n;
 int local;
 int view;

 if(stack[fp]==0) view=TXBFILE;
 else
 if(stack[fp]==1) view=RXBFILE;
 else             return(0);

 n=stack[fp+1]-1;
 local=stack[fp+2];

 if(n<0 || n>=vnofiles[view]) return(0);
 else
 {
  if(local) assignstring(stack[fp+3],vtable[view][n].lname);
  else      assignstring(stack[fp+3],vtable[view][n].rname);

  return(n+1);
 }
}


void batchremotename(int fp)
{
 int view;
 int n;

 if(stack[fp]==0) view=TXBFILE;
 else
 if(stack[fp]==1) view=RXBFILE;
 else             return;

 n=stack[fp+1]-1;

 if(n>=0 || n<vnofiles[view])
                strcpy(vtable[view][n].rname,stringptr(stack[fp+2]));
}


void batchsetstate(int fp)
{
 int view;
 int n;

 if(stack[fp]==0) view=TXBFILE;
 else
 if(stack[fp]==1) view=RXBFILE;
 else             return;

 n=stack[fp+1]-1;

 if(n>=0 || n<vnofiles[view]) vtable[view][n].state=stack[fp+2];
}


int batchgetstate(int fp)
{
 int view;
 int n;

 if(stack[fp]==0) view=TXBFILE;
 else
 if(stack[fp]==1) view=RXBFILE;
 else             return(0);

 n=stack[fp+1]-1;

 if(n>=0 || n<vnofiles[view]) return(vtable[view][n].state);
 else                         return(0);
}


void setftpbuffersize(int fp)
{
 ftptxsize=stack[fp];
 ftprxsize=stack[fp+1];
}


